﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Text;
using System.Windows.Browser;
using DiscoveryLogic.UI.Layouts.Controls;
using System.Windows.Markup;

namespace DiscoveryLogic.UI.Layouts.Controls
{

    [ScriptableType] 
    public class ChartEventArgs : EventArgs 
    {
        public ChartEventArgs (  string series, string itemname , string itemvalue , int index , Point p ) 
        { 
            _seriesName = series; 
            _itemName = itemname ; 
            _index = index; 
            _itemvalue  = itemvalue ;
            _point = p; 
        } 

        string _itemName;
        [ScriptableMember] 
public string ItemName
{
  get { return _itemName; }
  set { _itemName = value; }
} 
        int _index ;
        [ScriptableMember] 
public int Index
{
  get { return _index; }
  set { _index = value; }
} 
        string _itemvalue ;
        [ScriptableMember] 
public string Itemvalue
{
  get { return _itemvalue; }
  set { _itemvalue = value; }
} 
        Point _point ;
        [ScriptableMember] 
public Point MouseLocation
{
  get { return _point; }
  set { _point = value; }
} 



        private string _seriesName;
        [ScriptableMember] 
        public string SeriesName
        {
            get
            {
                return _seriesName;
            }
            set
            {
                _seriesName = value;
            }
        }

    }
    [ScriptableType] 
    public abstract class XYChart : Control 
    {
        private static int _count = 0; 
        public static string GetNextInstanceIndex( string classname )
        {
            if (_count++ == 0)
                return classname;
            else
                return classname + _count++.ToString(); 
        } 

        protected string GetInstanceName()
        {
            return XYChart.GetNextInstanceIndex(this.GetType().Name); 
        } 
        protected const string DataCanvasName = "Data";
        protected const string TitleName = "Title";
        protected const string XAxisLabelsName = "XAxisLabels";
        protected const string YAxisLabelsName = "YAxisLabels";
        protected const string LegendCanvasName = "LegendsCanvas";
        protected const string LegendPathPrefix = "legendPath"; 
        protected const string LegendTextPrefix = "legendText";
        protected const string LabelXName = "LabelX";
        protected const string LabelYName = "LabelY";
        protected const string UserDataName = "UserData";
        protected const string GridLinesName = "GridLines";

        private const string DataCanvasVerticalCursorName = "DataVerticalCursor"; 
        private const string DataCanvasHorizontalCursorName = "DataHorizontalCursor";
        private const string ToolTipName = "Tooltip";
        private const string ToolTipTextName = "TooltipText";


        private Canvas _canvasRoot;

        protected  Canvas CanvasRoot
        {
            get { return _canvasRoot; }
            set { _canvasRoot = value; }
        }
        protected Canvas _dataCanvas;
        protected Canvas _xAxisCanvas;
        protected Canvas _yAxisCanvas;
        protected Canvas _legendCanvas; 
        protected TextBlock _title;
        protected TextBlock _labelX;
        protected TextBlock _labelY;
        protected Canvas _toolTip;
        protected TextBlock _toolTipText;
        protected Canvas _userData;
        protected Canvas _gridLines; 



        //public XYChart()
        //{
        //    System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("DataViz.XYChart.xaml");
        //    this._canvasRoot = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd()) as Canvas;
        //    InitControls(); 
        //    this.Loaded += new EventHandler(OnLoaded);
           
        //}

        protected virtual void InitControls ( ) 
        { 
            _dataCanvas = (Canvas)this._canvasRoot.FindName(DataCanvasName);
            _title =  (TextBlock) this._canvasRoot.FindName(TitleName);
            _xAxisCanvas = (Canvas)this._canvasRoot.FindName(XAxisLabelsName);
            _yAxisCanvas = (Canvas)this._canvasRoot.FindName(YAxisLabelsName);
            _legendCanvas = (Canvas)this._canvasRoot.FindName(LegendCanvasName);
            _labelX = (TextBlock)this._canvasRoot.FindName(LabelXName);
            _labelY = (TextBlock)this._canvasRoot.FindName(LabelYName);
            _toolTip = (Canvas)this._canvasRoot.FindName(ToolTipName);
            _toolTipText = (TextBlock)this._canvasRoot.FindName(ToolTipTextName);
            _userData = (Canvas)this._canvasRoot.FindName(UserDataName);
            _gridLines = (Canvas)this._canvasRoot.FindName(GridLinesName); 
        } 

        void OnLoaded (object sender, EventArgs e)
        {            
            Draw(); 
        }

        [ScriptableMember] 
        public bool ShowYLabel
        {
            get
            {
                return this._labelY.Visibility == Visibility.Visible ; 
                
            }
            set
            {
                if (value)
                {
                    this._labelY.Visibility = Visibility.Visible;
                }
                else
                {
                    this._labelY.Visibility = Visibility.Collapsed;
                } 
            }
        }

        [ScriptableMember] 
        public bool ShowXLabel
        {
            get
            {
                return this._labelX.Visibility == Visibility.Visible;

            }
            set
            {
                if (value)
                    this._labelX.Visibility = Visibility.Visible;
                else
                    this._labelX.Visibility = Visibility.Collapsed;
            }
        }
        protected LineCursor _mouseCursor; 
        [ScriptableMember] 
        public string MouseCursor
        {
            get
            {
                return this._mouseCursor.ToString(); 

            }
            set
            {
                try
                {
                    LineCursor c = (LineCursor) Enum.Parse(typeof(LineCursor), value, true);
                    if (c != _mouseCursor)
                    {

                        ChangeCursor ( _mouseCursor, false ); 
                        ChangeCursor ( c , true ) ; 
                        _mouseCursor = c; 
                    }                    
                }
                finally { } 
            } 
        }

       



        protected  virtual void ChangeCursor ( LineCursor cursor, bool show ) 
        { 
            string elementName = string.Empty ;
            switch (cursor)
            {
                case LineCursor.VerticalLine:
                    elementName = DataCanvasVerticalCursorName;
                    break;
                case LineCursor.HorizontalLine:
                    elementName = DataCanvasHorizontalCursorName;
                    break; 
            }

            //this._cursor.Visibility = Visibility.Collapsed; 

            UIElement e = this._dataCanvas.FindName(elementName) as UIElement;
            if (e != null)
            {
                if (!show)
                    e.Visibility = Visibility.Collapsed;
                else
                    e.Visibility = Visibility.Visible;

                _cursor = e as Path; 
            } 
        }

        protected Path _cursor = null; 

       

        protected Color [] colors = new Color []
                          { 
                             Color.FromArgb(255, 0x2E, 0x5B, 0x95) ,  //Blue
                            Color.FromArgb(255, 0xC5, 0x5E, 0x93) ,  //Pink
                            Color.FromArgb(255, 0x7F, 0x4B, 0x92) ,  //Purple
                            Color.FromArgb(255, 0x44, 0x89, 0x44) ,  //Green
                            Color.FromArgb(255, 0xC5, 0xA9, 0x5E)  
                          } ;


        
        public Brush Background
        {
            get
            {
                return this._canvasRoot.Background; 
            }
            set
            {
                this._canvasRoot.Background = value; 
            } 
        }

        private Brush _foreground; 
        public Brush Foreground
        {
            get
            {
                return _foreground; 
            }
            set
            {
                _foreground = value; 
            } 
        }

        private double _labelSize = 16.0; 
        public double LabelSize 
        {
            get
            {
                return _labelSize;                 
            }
            set
            {
                _labelSize = value;
                DrawLabels(); 
            } 
        } 

        [ScriptableMember] 
        public string Title
        {
            get
            {
                return this._title.Text; 
            }
            set
            {
                this._title.Text = value; 
            } 
        }

        [ScriptableMember] 
        public string LabelX
        {
            get
            {
                return this._labelX.Text; 
            }
            set
            {
                this._labelX.Text = value;
            }
        }

        [ScriptableMember] 
        public string LabelY
        {
            get
            {
                return this._labelY.Text;
            }
            set
            {
                this._labelY.Text = value;
            }
        }


        protected virtual void DrawLabels()
        {
            this._title.FontSize = LabelSize;
            this._labelY.FontSize = LabelSize;
            this._labelX.FontSize = LabelSize;


            Helpers.CenterX(this._title, this._canvasRoot);
            Helpers.CenterX(this._labelX, this._canvasRoot);
            Helpers.CenterY(this._labelY, this._canvasRoot); 
           
        } 


        protected Brush GetBrush ( int index )
        {
            //return ChartBrushes.GetBrush(index); 
            return new SolidColorBrush ( colors [index%colors.Length]); 
        } 

#if FALSE 
        public PointSeries AddSeries(string s, string seriesName )
        {
            string[] r = s.Split(','); 

            PointSeries pointseries = new PointSeries ();
            double xMin = double.PositiveInfinity; 
            double yMin = Double.PositiveInfinity; 
            double xMax = double.NegativeInfinity; 
            double yMax = double.NegativeInfinity ; 

            for (int x = 0; x < r.Length -1; x += 2 )
            {
                double dx = System.Convert.ToDouble(r[x]); 
                double dy=  System.Convert.ToDouble(r[x+1]);
                pointseries.Points.Add ( new Point (dx,dy)) ; 
                xMin = Math.Min ( dx, xMin ); 
                xMax = Math.Max ( dx, xMax ); 
                yMin = Math.Min ( dy, yMin ); 
                yMax = Math.Max ( dy, yMax ); 
            }
            pointseries.MaxX = xMax;
            pointseries.MaxY = yMax;
            pointseries.MinX = xMin;
            pointseries.MinY = yMin;
            pointseries.Name = seriesName; 

            InternalSeries.Add ( new PointSeriesLineData  ( pointseries )) ; 

            return pointseries ;            
        }
#endif 

        
        //TODO: does this need to be exposed back 
        //public List<PointSeries> Series
        //{
        //    get {
        //        List<PointSeries> list = new List<PointSeries>();

        //        foreach (PointSeriesLineData linedata in InternalSeries)
        //        {
        //            list.Add(linedata.series);
        //        }

        //        return list; 
        //    }
       
        //}

        protected List<PointSeriesLineData> InternalSeries
        {
            get
            {
                return _series; 
            }
        } 
        private List<PointSeriesLineData> _series = new List<PointSeriesLineData>(); 


        //TODO: should this be abstract?? 
        protected virtual void GetEnds ( System.Collections.IList series , out double  xMin, out double xMax ,
            out double yMin, out double yMax) 
        { 
            xMin= double.PositiveInfinity ; yMin= double.PositiveInfinity  ;
            xMax = double.NegativeInfinity ; yMax = double.NegativeInfinity;

        }   
 
        protected virtual void GetScale(System.Collections.IList series , double plotSpaceWidth, double height,   
            double xLabelWidth , double yLabelHeight , 
            out double scaleX, out double scaleY, out double startX, out double endX,  out double startY , out double endY )
        {

            double xMin, xMax, yMin, yMax; 
            GetEnds (  series, out xMin, out xMax, out yMin, out yMax ) ; 

            double xRange = xMax - xMin;
            double yRange = yMax - yMin; 
            if (xRange > 10)
            {
                double d =  Math.Pow(10, Math.Max ( Math.Floor ( Math.Log10(xMin)), 1));                 
                if ( d != 0 )
                    xMin -= (xMin % d);

                d = Math.Pow(10, (Math.Floor(Math.Log10(xMax))));
                if (d != 0 && xMax % d != 0)
                {
                    xMax += (d - (xMax % d));
                } 

            }

            yMin = 0; 

            if (yRange > 10)
            {
                double d = Math.Pow(10, Math.Max(Math.Floor(Math.Log10(yMin)), 1));
                if (d != 0)
                    yMin -= (yMin % d);

                d = Math.Pow(10, (Math.Floor(Math.Log10(yMax))));
                if (d != 0 && yMax % d != 0)
                {
                   yMax += (d - (yMax % d));
                } 
            }
            
            
            scaleX = (plotSpaceWidth) / (xMax - xMin);
            scaleY = (height ) /(yMax - yMin) ;


            startX = xMin;
            endX = xMax; 
            startY = yMin;
            endY = yMax ; 

        }

        protected virtual void ScaleToSize ( ) 
        {
            double asX = this.Width / this.CanvasRoot.Width;
            double asY = this.Height / this.CanvasRoot.Height;

            ScaleTransform sc = CanvasRoot.RenderTransform as ScaleTransform;
            if (sc == null)
            {
                CanvasRoot.RenderTransform = sc = new ScaleTransform();
            } 

            if (asY < asX)
            {
               
                sc.ScaleX = asY;
                sc.ScaleY = asY; 
            }
            else
            {                
                sc.ScaleX = asX;
                sc.ScaleY = asX; 
            }  
        }

        //protected void GetScale(List<PointSeries> series, double plotSpaceWidth, double height,
        //    double xLabelWidth, double yLabelHeight,
        //    out double scaleX, out double scaleY, out double startX, out double endX, out double startY, out double endY)
        //{
        //    double xMin = double.PositiveInfinity, yMin = double.PositiveInfinity;
        //    double xMax = double.NegativeInfinity, yMax = double.NegativeInfinity;
        //    foreach (PointSeries p in series)
        //    {
        //        xMin = Math.Min(xMin, p.MinX);
        //        xMax = Math.Max(xMax, p.MaxX);

        //        yMin = Math.Min(yMin, p.MinY);
        //        yMax = Math.Max(yMax, p.MaxY);

        //    }


        //    double xRange = xMax - xMin;
        //    double yRange = yMax - yMin;
        //    if (xRange > 10)
        //    {
        //        double d = Math.Pow(10, (Math.Floor(Math.Log10(xRange))));
        //        if (d > xRange * .6)
        //        {
        //            d = Math.Max(d * 0.9, xRange * 0.6);
        //            d = Math.Pow(10, (Math.Floor(Math.Log10(d))));
        //        }

        //        xMin -= (xMin % d);
        //        xMax += (d - (xMax % d));
        //    }

        //    if (yRange > 10)
        //    {
        //        double d = Math.Pow(10, (Math.Floor(Math.Log10(yRange))));
        //        if (d > yRange * .6)
        //        {
        //            d = Math.Max(d * 0.9, yRange * 0.6);
        //            d = Math.Pow(10, (Math.Floor(Math.Log10(d))));
        //        }

        //        yMin -= (yMin % d);
        //        yMax += (d - (yMax % d));
        //    }


        //    scaleX = (plotSpaceWidth) / (xMax - xMin);
        //    scaleY = (height) / (yMax - yMin);


        //    startX = xMin;
        //    endX = xMax;
        //    startY = yMin;
        //    endY = yMax;

        //} 

        protected double _scaleX;
        protected double _scaleY;
        protected double _startPointX;
        protected double _startPointY;
        protected double _endPointY; 


        protected abstract void Draw(); 
        // {           
            //double scaleX;
            //double scaleY;
            //double startPointX = 0;
            //double startPointY = 0;
            //double endPointX = 0 ; 
            //double endPointY = 0; 

            //double width = this._canvasRoot.Width;
            //double height = this._canvasRoot.Height;

            //double xLabelWidth = 20;
            //double yLabelHeight = 20; 

            //GetScale( InternalSeries, _dataCanvas.Width, _dataCanvas.Height , 
            //    xLabelWidth, yLabelHeight , 
            //    out scaleX, out scaleY, out startPointX , out endPointX , 
            //    out startPointY , out endPointY );

            //this._scaleX = scaleX;
            //this._scaleY = scaleY;
            //this._startPointX = startPointX;
            //this._startPointY = startPointY;
            //this._endPointY = endPointY; 

            //DrawLegends();
            //DrawScale(  startPointX , endPointX , startPointY , endPointY , scaleX , scaleY );

            //for (int x = 0; x < InternalSeries.Count; x++)
            //{
            //    Path path = DrawSeries( InternalSeries[x].Series , scaleX , scaleY , startPointX , endPointY , GetBrush(x)  );

            //    InternalSeries[x].Path = path;
            //    path.MouseEnter += new MouseEventHandler(path_MouseEnter);
            //    path.MouseLeave += new EventHandler(path_MouseLeave); 


            //    this._dataCanvas.Children.Add(path); 
            //}
            //DrawLabels(); 
            
        // }

        protected abstract void ReDraw(); 

     
        protected virtual void DrawLegends()
        {

            string template =
            "<Canvas xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'  x:Name='legend___' Width='115' Height='25'>"
                + "<Path Fill='#FFFFFFFF' Stretch='Fill' Stroke='#FF000000' x:Name='legendPath___' Width='30' Height='1' Canvas.Left='4' Canvas.Top='14.5' Data='M4.5,15 L25.868421,15'/>"
                + "<TextBlock x:Name='legendText___' Width='87' Height='15' Canvas.Left='39' Canvas.Top='7' FontSize='12' TextWrapping='Wrap' ></TextBlock>"
            + "</Canvas>";

           
            double leftoffset = 0;
            double topoffset = 0;
           
            for (int x = 0; x < InternalSeries.Count; x++)
            {
                string canvas = template.Replace("___", x.ToString());
                Canvas c = XamlLoader<Canvas>.Create (canvas) ;

                Path p = c.FindName(LegendPathPrefix + x.ToString()) as Path;
                if (p != null) p.Stroke = GetBrush( x );
                TextBlock t = c.FindName(LegendTextPrefix + x.ToString()) as TextBlock;
                if (t != null) t.Text = InternalSeries[x].Series.Name;

                if (leftoffset + c.Width >= this._legendCanvas.Width)
                {
                    leftoffset = 0;
                    topoffset  += c.Height;
                } 
            

                c.SetValue (Canvas.LeftProperty, leftoffset );
                leftoffset += c.Width; 

                c.SetValue (Canvas.TopProperty,  topoffset ); 
                this._legendCanvas.Children.Add(c); 
            }             
        }

        protected virtual void DrawScale2(double minX, double maxX, double minY, double maxY, double scaleX, double scaleY)
        {
            //TextBlock measureText = new TextBlock();
            //measureText.FontSize = 10; 
            //measureText.Text = maxX.ToString() ;
            //double labelWidth = measureText.ActualWidth;
            //double labelHeight = measureText.ActualHeight;

            //double scaledwidth = this._xAxisCanvas.Width / scaleX;

            //int xLabels = System.Convert.ToInt32(this._xAxisCanvas.Width / (labelWidth * 1.4));

            //if (xLabels > 10)
            //{
            //    xLabels = 10; 
            //} 
            //int step = System.Convert.ToInt32(this._xAxisCanvas.Width / xLabels);
            //int scaledstep ; //  = System.Convert.ToInt32(step / scaleX); 

            //double centerX = labelWidth/2; 
            //for ( int x = 1 ; x < xLabels ; x++ ) 
            //{ 
            //    TextBlock t = new TextBlock();
            //    t.FontSize = 10; 
            //    t.Text = ( minX + (scaledstep*x)).ToString() ;
            //    t.SetValue (Canvas.LeftProperty, (step * x) - (t.ActualWidth / 2)); 
            //    this._xAxisCanvas.Children.Add ( t );
            //    if (ShowVerticalGridLines)
            //    {
            //        Line l = new Line();
            //        l.X1 = (step * x); l.Y1 = 0;
            //        l.X2 = (step * x); l.Y1 = this._dataCanvas.Height;
            //        l.Stroke = new SolidColorBrush(Colors.Gray);
            //        l.StrokeThickness = 1;
            //        this._dataCanvas.Children.Add(l);
            //    } 
            //}


            //measureText.Text = maxY.ToString();
            //labelWidth = measureText.ActualWidth;
            //labelHeight = measureText.ActualHeight;

            //double scaledheight = this._xAxisCanvas.Height/ scaleY;

            //int yLabels = System.Convert.ToInt32(this._yAxisCanvas.Height/ (labelHeight* 1.6));
            //if (yLabels > 10)
            //{
            //    yLabels = 10;
            //} 
            //step = System.Convert.ToInt32(this._yAxisCanvas.Height / yLabels);
            //scaledstep = System.Convert.ToInt32(step / scaleY);

           
            //for (int x = 1; x < yLabels; x++)
            //{
            //    TextBlock t = new TextBlock();
            //    t.FontSize = 10; 
            //    t.Text = (maxY - (scaledstep * x)).ToString();
            //    t.SetValue (Canvas.TopProperty, (step * x) - (t.ActualHeight/2));                
            //    this._yAxisCanvas.Children.Add(t);

            //    if (ShowHorizontalGridLines)
            //    {
            //        Line l = new Line();
            //        l.X1 = 0; l.Y1 = (step * x);
            //        l.X2 = this._dataCanvas.Width; ; l.Y2 = (step * x);
            //        l.Stroke = new SolidColorBrush(Colors.Gray);
            //        l.StrokeThickness = 1;
            //        this._dataCanvas.Children.Add(l);
            //    }
            //} 

        }

        private bool _showVerticalGridLines = true ;

        [ScriptableMember] 
        public bool ShowVerticalGridLines
        {
            get { return _showVerticalGridLines; }
            set {

                bool old = _showVerticalGridLines;
                _showVerticalGridLines = value;
                if (old != _showVerticalGridLines)
                    ReDraw(); 
                _showVerticalGridLines = value; 
            }
        }


         
        private bool _showHorizontalGridLines = true;

        [ScriptableMember]
        public bool ShowHorizontalGridLines
        {
            get { return _showHorizontalGridLines; }
            set { 
                     
                    bool old = _showHorizontalGridLines; 
                    _showHorizontalGridLines= value;
                if (old != _showHorizontalGridLines)
                    ReDraw(); 
            
            }
        }


        private double _leftOffset = 30;

        public double LeftOffset
        {
            get { return _leftOffset; }
            set { _leftOffset = value; }
        }


        private double _topOffset = 0;

        public double TopOffset
        {
            get { return _topOffset; }
            set { _topOffset = value; }
        }


         

        protected Path DrawSeries( PointSeries p, double scaleX , double scaleY , double startPointX, double startPointY , Brush strokeBrush)
        {                   
            StringBuilder sb = new StringBuilder();
             

            double startX = ((p.Points[0].X - startPointX) * scaleX) ;
            double startY = ((startPointY - p.Points[0].Y) * scaleY)  ; 


            sb.Append("<Path  StrokeThickness='2' Data='M " + startX.ToString() + "," + startY.ToString() + " L "); 
            for ( int a = 1; a < p.Points.Count ; a++ ) 
            { 
                sb.AppendFormat ( " {0},{1} " ,                    
                        ( ( p.Points[a].X - startPointX ) * scaleX)   , 
                        ( ( startPointY - p.Points[a].Y ) * scaleY )                    
                    ); 
            }  
            sb.Append ( "' />" ); 

            Path path = XamlLoader<Path>.Create (sb.ToString());
            path.Stroke = strokeBrush;
            return path; 

        }

        void path_MouseLeave(object sender, EventArgs e)
        {
            this._toolTip.Visibility = Visibility.Collapsed;  
        }

        void path_MouseEnter(object sender, MouseEventArgs e)
        {
            Point datacoord = e.GetPosition(this._dataCanvas);
            // Don't show tooltip
            //this._toolTip.Visibility = Visibility.Visible;  
            this._toolTip.SetValue (Canvas.LeftProperty, datacoord.X + 3);
            this._toolTip.SetValue (Canvas.TopProperty, datacoord.Y + 3);

            this._toolTipText.Text =  string.Format 
            ( "{0:#},{1:#}" , ToXValue ( datacoord.X ) , ToYValue ( datacoord.Y)); 
                 
        }

        protected virtual double ToXValue(double x)
        {
            return x / this._scaleX + this._startPointX ; 
        }

        protected virtual double ToYValue(double y)
        {

            return (this._endPointY - (  y /  this._scaleY )) + this._startPointY; 
          
        } 


    }
}
